/* test_threads_func_uthread_yield():
	A test for uthread_yield().

	* Creates threads 1,2.
	* Each thread in turn does 'yield' - cycling all the threads few times.
	* Thread 0 cancels thread 1, and does join to thread 1. We then do the
	  cycling again.
	* Thread 0 cancels (wihtout joinining) thread 2, and we do cycling again.
*/

#include <stdio.h>
#include <stdbool.h>
#include "test_general.h"
#include "user_threads.h"

bool test_threads_func_uthread_yield();

#define THREADS_NUM 3


bool killed_thread_1_yet;
int last_thread_running;
	// This is either 0,1,2.

static int thread1_start_routine(void * param) {
	TEST_ASSERT( 53 == (*((int*)param)) );
	TEST_ASSERT (uthread_self() == 1);

	thread_ok[1] = true;
	while (true) {
		TEST_ASSERT(!killed_thread_1_yet);

		TEST_ASSERT(0 == last_thread_running);
		last_thread_running = 1;

		uthread_yield();
	}
}

static int thread2_start_routine(void * param) {
	TEST_ASSERT( 53 == (*((int*)param)) );
	TEST_ASSERT (uthread_self() == 2);

	thread_ok[2] = true;
	while (true) {
		if (killed_thread_1_yet) {
			TEST_ASSERT(0 == last_thread_running);
		} else {
			TEST_ASSERT(1 == last_thread_running);
		}
		last_thread_running = 2;

		uthread_yield();
	}
}


bool test_threads_func_uthread_yield()
{
	init_individual_test();

	int tmp, tmp2;

	if (initilize_uthread_in_each_test) {
		tmp = uthread_init();
		TEST_ASSERT(tmp == UTHREAD_SUCCESS);
	}


	thread_id threads[THREADS_NUM];
	threads[0] = uthread_self();


	int FiftyThree = 53;

	threads[1] = uthread_create(thread1_start_routine, &FiftyThree);
	TEST_ASSERT(threads[1] == 1);

	threads[2] = uthread_create(thread2_start_routine, &FiftyThree);
	TEST_ASSERT(threads[2] == 2);


	killed_thread_1_yet = false;
	last_thread_running = 0;
	uthread_yield();

	int i;
	for (i=0; i<20; i++){
		TEST_ASSERT(2 == last_thread_running);
		last_thread_running = 0;
		uthread_yield();
	}

	// Cancelling and joining thread 1
	tmp = uthread_cancel(threads[1]);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);
	tmp = uthread_join(threads[1],&tmp2);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);
	TEST_ASSERT(UTHREAD_CANCELLED == tmp2);
	killed_thread_1_yet = true;


	for (i=0; i<20; i++){
		TEST_ASSERT(2 == last_thread_running);
		last_thread_running = 0;
		uthread_yield();
	}


	// Cancelling thread 2
	tmp = uthread_cancel(threads[2]);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);

	TEST_ASSERT(2 == last_thread_running);
	last_thread_running = 0;
	for (i=0; i<20; i++){
		TEST_ASSERT(0 == last_thread_running);
		last_thread_running = 0;
		uthread_yield();
	}

	tmp = uthread_join(threads[2],&tmp2);
	TEST_ASSERT(UTHREAD_SUCCESS == tmp);
	TEST_ASSERT(UTHREAD_CANCELLED == tmp2);


	thread_ok[0] = true;
	TEST_ASSERT(are_all_threads_ok(THREADS_NUM));

	return true;
}
